import {
    Injectable,
    NestInterceptor,
    CallHandler,
    ExecutionContext, HttpException, HttpStatus, UnauthorizedException,
} from '@nestjs/common';
import { map } from 'rxjs/operators';
import * as requestIp from "request-ip";
import {
    handleCurTime,
    handleFilterObjectEmptyData,
    handleGetUrlParams,
    handleRecordLogs,
    securityMd5
} from "@/utils/utils";
import {LoginLogAddDto, OperationLogAddDto} from "@/modules/log/dto";
import {getConnection} from "typeorm";
import {OperationLogEntity} from "@/modules/log/entities/operationLog.entity";
import {UserEntity} from "@/modules/user/entities/user.entity";
import {LoginLogEntity} from "@/modules/log/entities/loginLog.entity";
import { operationTypeEnum, loginStatusEnum } from '@/common/enum';

type logType = "loginLog" | "operationLog"
type operationType = operationTypeEnum.add | operationTypeEnum.edit | operationTypeEnum.search | operationTypeEnum.delete | operationTypeEnum.other
interface dataInterface {
    data:any                    //日志具体操作内容
    logType:logType             //日志类型
    operationId?:number|string  //操作id
    operationType?:operationType//操作类型
}
//日志拦截器 记录操作日志
@Injectable()//依赖注入
export class LoggingInterceptor<T> implements NestInterceptor {
    private readonly data:dataInterface
    private operationId:any
    constructor(data: dataInterface) {
        this.data = data;
    }

    async intercept(context: ExecutionContext, next: CallHandler<T>): Promise<any> {
        console.log('LoggingInterceptor-Before...');
        const request = context.switchToHttp().getRequest();
        this.operationId = await this.handleToData(this.data,request,true)
        const now = Date.now();
        return next.handle().pipe(
            map(async (val) => {
                console.log(`LoggingInterceptor-After... ${Date.now() - now}ms`)
                // console.log(val,"valvalval")
                if(this.data.logType=="operationLog"){
                    await this.handleToData({data:JSON.stringify(val),logType:"operationLog",operationId:this.operationId},request,false)
                }
                return await new Promise((resolve, reject)=>{
                    resolve(val)
                })
            }),
        );
    }

    /**
     *
     * @param data
     * @param req
     * @param isAdd 操作具体类型  true 添加 false 编辑更新
     */
    async handleToData (data:dataInterface,req,isAdd?:boolean):Promise<any> {
        let operationContent = data.data
        let logType = data.logType
        let operationType = data.operationType

        let clientIp = requestIp.getClientIp(req)

        if(logType == "loginLog"){
            let { username,password,operationSystem,browser,isPcOrIphone } = req.body
            //在白名单加入访问记录
            handleRecordLogs(1,{url:req.url,clientIp})
            //插入操作访问数据库中
            let userData = await this.userDetailByUsernameAndPwd(username,password)
            //插入操作日志数据库中
            await this.loginLogAdd({uid:userData?.id,ip:clientIp,operationSystem,browser,isPcOrIphone,status:userData?.id?loginStatusEnum.success:loginStatusEnum.fail});
            if(!userData?.id){
                throw new HttpException("账号密码错误",HttpStatus.SERVICE_UNAVAILABLE)
            }
            return true;
        }else if(logType == "operationLog"){
            if(isAdd){
                let operationSystem,browser="";
                let isPcOrIphone=0;
                let uid=0;
                if(req.method.toLowerCase()=="get"){
                    operationSystem =handleGetUrlParams(req.url, "operationSystem")
                    browser =handleGetUrlParams(req.url, "browser")
                    isPcOrIphone =parseInt(handleGetUrlParams(req.url, "isPcOrIphone"))
                    uid =parseInt(handleGetUrlParams(req.url, "uid"))
                }else{
                    operationSystem =req.body.operationSystem
                    browser =req.body.browser
                    isPcOrIphone =req.body.isPcOrIphone
                    uid =req.body.uid
                }
                //无差别加入操作记录
                handleRecordLogs(2,{detail:"请求url:"+req.url+" 请求ip:"+clientIp+" 操作类型:"+operationType+" 详细操作:"+operationContent,status:"success"})
                if (uid > 0) {
                    let requestMethod = req.method
                    let requestParams = ""
                    if(requestMethod.toLowerCase()=="post"){
                        requestParams = req.body
                    }else{
                        requestParams = req.query
                    }
                    let requestUrl = req.originalUrl
                    let addContent:OperationLogAddDto = {
                        respondParams: "",
                        requestParams:requestParams?JSON.stringify(requestParams):"",
                        operationContent,
                        operationType,
                        requestMethod,
                        requestUrl,
                        requestIp:clientIp,
                        uid,
                        operationSystem,
                        browser,
                        isPcOrIphone
                    }
                    //插入操作日志数据库中
                    let operationId = await this.operationLogAdd(addContent);
                    console.log(operationId,"operationIdoperationIdoperationId");
                    return operationId;
                }else{
                    throw new UnauthorizedException('请先登录');
                }
            }else{
                let operationId = data.operationId
                //更新操作日志响应数据
                await this.operationLogUpdateResponse(data.data,operationId);
                return true;
            }
        }
    }
    /**
     * 操作添加
     * @param operationLogAddDto
     */
    async operationLogAdd (operationLogAddDto:OperationLogAddDto) {
        let filterData = handleFilterObjectEmptyData(operationLogAddDto);
        let operationData;
        try {
            operationData = await getConnection().createQueryBuilder().from(OperationLogEntity, 'operationLog').insert().values({...filterData}).execute()
        }catch (error) {
            throw new HttpException(error,HttpStatus.SERVICE_UNAVAILABLE)
        }
        return operationData?.identifiers[0]["id"];
    }
    /**
     * 更新操作日志响应数据
     * @param respondParams
     * @param id
     */
    async operationLogUpdateResponse (respondParams:any,id:number|string) {
        let updateTime = handleCurTime();
        try {
            await getConnection().createQueryBuilder().from(OperationLogEntity, 'operationLog').where("id = :id",{id}).update().set({updateTime,respondParams,status:loginStatusEnum.success}).execute()
        }catch (error) {
            throw new HttpException(error,HttpStatus.SERVICE_UNAVAILABLE)
        }
        return true;
    }
    /**
     * 通过用户id查找用户详情
     */
    async userDetailByUsernameAndPwd (username:string,password:string):Promise<UserEntity | undefined> {
        let originalPwd = password
        password = securityMd5(password)
        let userDetail  =  await getConnection().createQueryBuilder().from(UserEntity, 'user').where({username,password,originalPwd}).getRawOne()
        return userDetail
    }

    /**
     * 访问添加
     * @param loginLogAdd
     */
    async loginLogAdd (loginLogAdd: LoginLogAddDto) {
        let filterData = handleFilterObjectEmptyData(loginLogAdd);
        try {
            await getConnection().createQueryBuilder().from(LoginLogEntity, 'loginLog').insert().values({...filterData}).execute()
        }catch (error) {
            throw new HttpException(error,HttpStatus.SERVICE_UNAVAILABLE)
        }
        return true;
    }
}